#ifndef QRemoteObject_H
#define QRemoteObject_H
#include <QList>
#include <QVariant>
#include <QHash>
#include <QPointer>
#include <QVariantList>
#include <QMetaObject>
#include "QRemoteObjectAdvance_global.h"

class QRemoteObject2Meta;
class QRemoteObject2Private;
class QROInterface;
class QREMOTEOBJECTADVANCE_EXPORT QRemoteObject2
{
public:
    enum class ROSignalBehavior
    {
        None,
        ServerOnly,//仅在服务端调用
        ClientOnly,//仅在所有客户端调用
        OwnerOnly,//仅在所有者上调用
        BroadCast,//服务端和所有客户端调用
    };

    enum class ROPropertyFlag
    {
        None,
        ReadOnly,//客户端只能接收服务端的数据，不能更改该数据,默认只读
        ReadWrite//客户端既可以接收服务端的数据，也能上传更改数据
    };

    class HierarchyInterface;

private:
    struct ROMethodMetaData{
        int index = -1;
        ROSignalBehavior behaiovr = ROSignalBehavior::None;
    };

    struct ROPropertyMetaData{
        int index = -1;
        ROPropertyFlag flag = ROPropertyFlag::None;
    };

    struct RemoteObjectMetaData{
        const QMetaObject* meta;

        std::function<QObject*()> constructor;
        std::unordered_map<int,ROMethodMetaData> methods;
        std::unordered_map<int,ROPropertyMetaData> propertys;
        std::shared_ptr<HierarchyInterface> hierarchyInterface;

    };
    typedef std::shared_ptr<RemoteObjectMetaData> RemoteObjectMetaDataPtr;


public:

    //父级访问器的工厂
    class QREMOTEOBJECTADVANCE_EXPORT HierarchyInterfaceFactory
    {
    public:
        ~HierarchyInterfaceFactory(){};

        virtual HierarchyInterface* createInstance(const QMetaObject* meta) = 0;

    };

    //因为不同的模块对父级的定义有所不同，所以需要该模块来兼容操作
    /*父级访问器可以通过以下方式注册，优先级由高到低
     * 1.注册时可以直接通过注册器设置父级访问器
     * 2.为某个类注册默认父级访问器，该类的子类注册的时候会默认使用该父级访问器
     * 3.本系统提供三种内嵌父级访问器，QObject访问器，QNode访问器，QQuickItem访问器
     * （没有QWidget访问器是因为它没有将读写接口通过反射的形式提供出来，我又不想为了一个可有可无的库增加依赖需求)
     * 当以上条件都不满足时，自动获取上述访问器的其一（根据继承关系）
    */
    class QREMOTEOBJECTADVANCE_EXPORT HierarchyInterface
    {
        public:
        virtual ~HierarchyInterface(){}

        //设置父级，若设置失败则返回
        virtual bool setParent(QObject* target,QObject* newParent) = 0;
        //获取父级
        virtual QObject* parent(QObject* target) = 0;
        //从一个对象中获取父级修改信号
        virtual int parentNotifier(QObject* target) = 0;
    };
    //注册一个类，从该类的元信息中自动获取同步属性以及远程函数,也可以增加你自定义的属性和函数
    class QREMOTEOBJECTADVANCE_EXPORT RemoteClassRegister
    {
    public:
        //添加信号和属性
        void addSignal(int index,ROSignalBehavior behavior);
        void addProperty(int index,ROPropertyFlag flag = ROPropertyFlag::ReadOnly);
        void finish();
    private:
        RemoteClassRegister(const QMetaObject* meta,std::function<QObject*()> constructor);
        friend class QRemoteObject2;
        RemoteObjectMetaDataPtr data;
    };

    static RemoteClassRegister registerRemoteClass(const QMetaObject* meta,std::function<QObject*()> constructor);

    template<typename T>
    static RemoteClassRegister registerRemoteClass(){
        //将该类型的指针注册
        int type = qRegisterMetaType<T*>();
        //为了确保qpointer能在两端之间转换，需要注册转换函数
        QMetaType::registerConverter<QPointer<T>,QPointer<QObject>>([](const QPointer<T>& input){
            return QPointer<QObject>(qobject_cast<QObject*>(input.get()));
        });
        QMetaType::registerConverter<QPointer<QObject>,QPointer<T>>([](const QPointer<QObject>& input){
            return QPointer<T>(qobject_cast<T*>(input.get()));
        });

        return registerRemoteClass(&T::staticMetaObject,[](){return new T();});
    }

    //推送属性。当一个对象的属性被设置为了可读写时，客户端可以修改该属性。但必须使用该函数进行修改，直接修改副本属性无效
    template<typename T>
    static void pushProperty(QObject* target,QString prop,T value){
        QVariant var;
        if constexpr (std::is_same<T,const char*>::value)
        {
            var = QVariant::fromValue(QString(value));
        }
        else
        {
            var = QVariant::fromValue(value);
        }

        auto meta = target->metaObject();
        auto propertyIndex = meta->indexOfProperty(prop.toLocal8Bit());



        pushProperty(target,propertyIndex,var);
    }
    static void pushProperty(QObject* target,int index,QVariant value);


    typedef std::function<void(const QVariant& ret,bool success,QString reason)> InvokeCallBack;
    //调用远程函数
    //若callBack为非空，则会在远程函数调用完毕后执行。若远程函数有返回值 则会一并通过callBack获取到
    //callBack仅对执行方唯一的调用起效(Server,OwnerOnly)，其它情况会直接调用失败。
    //若callBack为空，则说明调用方不在乎返回值，本端就不会监听结果，能够提升性能
    static void invoke(QObject* target,int index,QVariantList args,InvokeCallBack callBack = {});

    //发射函数，不在乎返回值和是否执行完毕
    template<typename ...Args>
    static void emits(QObject* target,QString signalName,Args... args){
        invoke(target,signalName,{},args...);
    }

//    //发射函数，不在乎返回值和是否执行完毕
    //暂且禁用，因为槽函数无法通过函数地址查询到id
//    template<typename T,typename ...Args>
//    static void emits(QObject* target,void (T::*sigFunc)(Args...),Args... args)
//    {
//        invoke(target,sigFunc,{},args...);
//    }

    //调用函数，在乎返回值和是否执行完毕
    template<typename ...Args>
    static void invoke(QObject* target,QString signalName,InvokeCallBack callBack,Args... args){
        auto meta = target->metaObject();
        auto index = meta->indexOfMethod(signalName.toLocal8Bit());
        if(index == -1)
        {
            if(callBack)
            {
                callBack({},false,QStringLiteral("不存在该函数"));
            }
            return;
        }
        invoke(target,index,QVariantList{QVariant::fromValue(args)...},callBack);
    }

    //调用函数，在乎返回值和是否执行完毕
    //暂且禁用，因为槽函数无法通过函数地址查询到id
//    template<typename T,typename R,typename ...Args>
//    static void invoke(QObject* target,R (T::*sigFunc)(Args...),InvokeCallBack callBack,Args... args)
//    {
//        const QMetaObject* meta = target->metaObject();
//        void ** signalParam = reinterpret_cast<void **>(&sigFunc);
//        int signal_index = -1;
//        void *param[] = { &signal_index, signalParam };


//        meta->static_metacall(QMetaObject::IndexOfMethod,0,param);
//        auto index = *(int*)param[0];
//        if(index == -1)
//        {
//            if(callBack)
//            {
//                callBack({},false,QStringLiteral("不存在该函数"));
//            }
//            return;
//        }

//        index += meta->methodOffset();

//        invoke(target,index,QVariantList{QVariant::fromValue(args)...},callBack);
//    }

    //设置某对象的父级
    static void setParent(QObject* target,QObject* parent);

    //占领或释放一个对象，
    //一个对象默认归属于服务器，当客户端提出占领请求（occupyObject）时，服务端可以将该对象交由客户端管理
    //当客户端提出释放请求时（releaseObject），服务端将该对象回收权限
    //一个已经被客户端占领的对象,是无法被另一个客户端再次占领的，但是可以被服务器随时收回权限
    //在服务端以下两个函数效果相同，都是将对象权限收回
    //在客户端，当提出占领或释放请求时，在回调函数执行前 再次提出的占领或释放请求直接视为失败
    //一个客户端同时只能占领一个对象，占领新的对象会导致旧的对象失去占领权
    //被占领的对象和其所有子对象都归属占领的客户端
    //具有归属权的客户端能够调用OwnerOnly相关的功能。

//    enum OccupyState{
//        OccupySuccess,//对象占领成功
//        ReleaseSuccess,//对象释放成功
//        OccupyFailed,//对象占领失败
//        ReleaseFailed,//对象释放失败
//        ReclaimByServer//被服务器收归了（服务器主动占领，或是已经占领了一个对象的客户端尝试再占领另一个对象，对于前一个对象来说会被服务器收回）
//    };

    static void occupyObject(QObject* target);
    static void releaseObject(QObject* target);
        //获取占领的对象，仅客户端有用
    static QObject* occupiedObject(QObject* target);

    //获取对象所对应的网络接口（ROHost或RONode）
    static QROInterface* getNetInterface(QObject* target);

    //判断是否是服务端或客户端
    static bool isSource(QObject* target);
    static bool isReplica(QObject* target);



    //将对象从网络接口中注销
    static void unregister(QObject* target);

    template<typename T>
    static void registerHierarchyFactory(HierarchyInterfaceFactory* factory){
        registerHierarchyFactory(&T::staticMetaObject.className(),factory);
    }

    //为某个类注册父级访问器的工厂，所有该子类注册时会自动获取父级访问器
    static void registerHierarchyFactory(const char* className,HierarchyInterfaceFactory* factory);

private:
    friend class QRemoteObject2Meta;
    friend class QRemoteObject2Private;
};

#define REMOTE_PROP_FLAG_READONLY "READONLY"

#define REMOTE_PROP_FLAG_READWRITE "READWRITE"

//声明一个远程属性
#define REMOTE_PROPERTY(name,flag) \
Q_CLASSINFO("QtRO.Property." \
#name,flag) \

#define REMOTE_PROP_FLAG_SERVER_ONLY "SERVERONLY"
#define REMOTE_PROP_FLAG_CLIENT_ONLY "CLIENTONLY"
#define REMOTE_PROP_FLAG_OWNER_ONLY "OWNERONLY"
#define REMOTE_PROP_FLAG_BROAD_CAST "BROADCAST"

//声明一个远程信号
#define REMOTE_SIGNAL(signal,flag,...) \
Q_CLASSINFO("QtRO.Method." \
#signal \
"(" \
#__VA_ARGS__ \
")" \
,flag) \
Q_SIGNAL void signal(__VA_ARGS__);\

//定义一个远程槽
#define REMOTE_SLOT(slot,retType,flag,...) \
Q_CLASSINFO("QtRO.Method." \
#slot \
"(" \
#__VA_ARGS__ \
")" \
,flag) \
Q_INVOKABLE retType slot(__VA_ARGS__);\


#endif
